﻿using System;
using System.Text;
using System.Threading;
using Microsoft.Ccr.Core;
using NUnit.Framework;
using XcoAppSpaces.Core;
using XcoAppSpaces.Dialogs;

namespace test.Dialogs
{
    [TestFixture]
    public class Test_DialogHost
    {
        [Test]
        public void Create_dialog_host()
        {
            new DialogHost();
        }


        [Test]
        public void Start_dialog_with_automatic_creation()
        {
            var dhServer = new DialogHost();
            dhServer.Register<AutoResetEvent, MyDialogServer>();

            var are = new AutoResetEvent(false);
            var dhClient = new DialogHost();
            var dlgClient = (MyDialogClient)dhClient.StartDialog(are, typeof(MyDialogClient), dhServer);

            Assert.IsTrue(are.WaitOne(500));
            Assert.IsTrue(dlgClient.localAre.WaitOne(500));
            Assert.AreEqual(are, dlgClient.areReceived);
        }


        [Test]
        public void Start_dialog_with_instance()
        {
            var dhServer = new DialogHost();
            dhServer.Register<AutoResetEvent, MyDialogServer>();

            var dlgClient = new Dialog();
            var areClient = new AutoResetEvent(false);
            dlgClient.Handler<AutoResetEvent>(areStart => areClient.Set());

            var are = new AutoResetEvent(false);
            var dhClient = new DialogHost();
            dhClient.StartDialog(are, dlgClient, dhServer);

            Assert.IsTrue(areClient.WaitOne(500));
            Assert.IsTrue(are.WaitOne(500));
        }


        [Test]
        public void Start_dialog_by_selecting_dialog_type_from_registered()
        {
            var dhServer = new DialogHost();
            dhServer.Register<AutoResetEvent, MyDialogServer>();

            var are = new AutoResetEvent(false);
            var dhClient = new DialogHost();
            dhClient.Register<AutoResetEvent, MyDialogClient>();
            var dlgClient = (MyDialogClient)dhClient.StartDialog(are, dhServer);

            Assert.IsTrue(are.WaitOne(500));
            Assert.IsTrue(dlgClient.localAre.WaitOne(500));
            Assert.AreEqual(are, dlgClient.areReceived);
        }


        [Test]
        public void Register_dialog_with_factory_method()
        {
            var dhServer = new DialogHost();
            dhServer.Register<AutoResetEvent>(t => new MyDialogServer());

            var are = new AutoResetEvent(false);
            var dhClient = new DialogHost();
            dhClient.Register<AutoResetEvent>(t => new MyDialogClient());
            var dlgClient = (MyDialogClient)dhClient.StartDialog(are, dhServer);

            Assert.IsTrue(are.WaitOne(500));
            Assert.IsTrue(dlgClient.localAre.WaitOne(500));
            Assert.AreEqual(are, dlgClient.areReceived);
        }



        [Test]
        public void Run_dialog_back_and_forth()
        {
            var dhServer = new DialogHost();
            dhServer.Register<AutoResetEvent, MyPingPongConversationServer>();

            var are = new AutoResetEvent(false);
            var dhClient = new DialogHost();
            dhClient.StartDialog(are, typeof(MyPingPongConversationClient), dhServer);

            Assert.IsTrue(are.WaitOne(500));
        }

        [Test]
        public void Run_dialog_hosts_in_appspace()
        {
            using (var space = new XcoAppSpace())
            {
				var dhServer = space.RunWorker(new DialogHost(space.DefaultDispatcherQueue), "dhServer");
                dhServer.Register<AutoResetEvent, MyDialogServer>();

				var dhClient = space.RunWorker(new DialogHost(space.DefaultDispatcherQueue), "dhclient");

                var are = new AutoResetEvent(false);
                dhClient.StartDialog(are, typeof(MyDialogClient), dhServer);

                Assert.IsTrue(are.WaitOne(500));
            }
        }

        [Test]
        public void Run_distributed_dialog()
        {
            using(var server = new XcoAppSpace("tcp.port=12345"))
            using(var client = new XcoAppSpace("tcp.port=0"))
            {
				DialogHost dhServer = server.RunWorker(new DialogHost(server.DefaultDispatcherQueue), "remotedialoghost");
                dhServer.Register<int, MyRemoteDialog>();

                var dhRemote = client.ConnectWorker<DialogHost>("localhost:12345/remotedialoghost");

                var dhClient = new DialogHost(client.DefaultDispatcherQueue);
                var dlgClient = dhClient.StartDialog<MyLocalDialog>(1, dhRemote);

                Assert.IsTrue(dlgClient.are.WaitOne(500));
            }
        }
    }


    class MyDialogClient : Dialog
    {
        public AutoResetEvent areReceived;
        public AutoResetEvent localAre = new AutoResetEvent(false);

        protected override void Define()
        {
            Handler<AutoResetEvent>(are => { this.areReceived = are; localAre.Set(); });
        }
    }

    class MyDialogServer : Dialog
    {
        protected override void  Define()
        {
            Handler<AutoResetEvent>(are => are.Set());
        }
    }


    class MyPingPongConversationClient : Dialog
    {
        protected override void Define()
        {
            Handler<int>(i => Reply((double)(i+1)));
            Handler<string>(s => Reply(new StringBuilder(s + "x")));
        }
    }

    class MyPingPongConversationServer : Dialog
    {
        private AutoResetEvent are;

        protected override void Define()
        {
            Handler<AutoResetEvent>(are => { this.are = are; Reply(1); });
            Handler<double>(d => Reply((d+1).ToString()));
            Handler<StringBuilder>(sb => { 
                                            Console.WriteLine("server: {0}", sb);
                                            are.Set(); 
                                         });
        }
    }


    class MyLocalDialog : Dialog
    {
        public AutoResetEvent are = new AutoResetEvent(false);
        public int received;

        protected override void Define()
        {
            Handler<int>(i => { Console.WriteLine("client: {0}", i);
                                this.received = i;
                                this.are.Set(); });
        }
    }

    class MyRemoteDialog : Dialog
    {
        protected override void Define()
        {
            Handler<int>(i => { Console.WriteLine("server: {0}", i);
                                Reply(i + 1); });
        }
    }
}
